Find intersection between Terrain & Smart line
The below code snippet shows how to find the intersection between 3D line and a terrain surface. The 3D line here is a LineString object. LineString is a set of connected lines.
Example
//Required References
using System.Collections.Generic;
using Bentley.DgnPlatformNET;
using Bentley.DgnPlatformNET.Elements;
using Bentley.CifNET.GeometryModel.SDK;
using Bentley.CifNET.SDK;
using Bentley.CifNET.LinearGeometry;
//Element selected from UI of type LineString
Element el=…; //selected from UI
if (el.ElementType == MSElementType.LineString)
TerrainAndLineIntersection(el);
public void TerrainAndLineIntersection(Element el)
{
//Get active connection of dgn model
ConsensusConnection sdkCon = Bentley.CifNET.SDK.Edit.ConsensusConnectionEdit.GetActive();
if (sdkCon == null)
return;
//Get geometric model
GeometricModel geomModel = sdkCon.GetActiveGeometricModel();
if (geomModel == null)
return;
SurfaceEntity activeSurface = geomModel.ActiveSurface;
//Validate terrain surface and selected element
if (activeSurface != null && activeSurface is TerrainSurface && el.ElementType == MSElementType.LineString)
{
TerrainSurface surface = activeSurface as TerrainSurface;
List<LinearElement> segments = new List<LinearElement>();
//Get active Dgn model
DgnModel activeModel = Bentley.MstnPlatformNET.Session.Instance.GetActiveDgnModel();
//Get model info to get units
ModelInfo info = activeModel.GetModelInfo();
//Convert Element to LineString
Bentley.DgnPlatformNET.Elements.LineStringElement lineString = el as Bentley.DgnPlatformNET.Elements.LineStringElement;
List<Bentley.GeometryNET.DPoint3d> pointList = new List<Bentley.GeometryNET.DPoint3d>();
Bentley.GeometryNET.CurvePrimitive curvePrim = lineString.GetCurveVector().GetPrimitive(0);
curvePrim.TryGetLineString(pointList);
//Get LineString points
for (int i = 0; i < pointList.Count - 1; i++)
{
Bentley.GeometryNET.DPoint3d point1 = pointList[i];
Bentley.GeometryNET.DPoint3d point2 = pointList[i + 1];
//Convert units to meter
point1.Set(point1.X / info.UorPerMeter, point1.Y / info.UorPerMeter, point1.Z / info.UorPerMeter);
point2.Set(point2.X / info.UorPerMeter, point2.Y / info.UorPerMeter, point2.Z / info.UorPerMeter);
//Create linear elements after converting units
segments.Add(new Line3d(point1, point2));
}
//For each linear element find intersection of it with terrain
foreach (LinearElement le in segments)
{
//Get intersecting line here
Bentley.GeometryNET.DSegment3d intersectLine = new Bentley.GeometryNET.DSegment3d();
//Call IntersectVector()
bool intersects = IntersectVector(surface.DTM, out intersectLine, le.StartPoint.Coordinates, le.EndPoint.Coordinates);
}
}
}
public bool IntersectVector(Bentley.TerrainModelNET.DTM dtm, out Bentley.GeometryNET.DSegment3d intersectionSegment, Bentley.GeometryNET.DPoint3d startPt, Bentley.GeometryNET.DPoint3d endPt)
{
intersectionSegment = new Bentley.GeometryNET.DSegment3d();
//Project the start and end points of line to terrain surface
var drapeResult = dtm.DrapeLinearPoints(new Bentley.GeometryNET.DPoint3d[] { startPt, endPt });
double vectorDist = startPt.DistanceXY(endPt);
double vectorZDiff = endPt.Z - startPt.Z;
bool hasPrevPt = false;
Bentley.GeometryNET.DPoint3d prevPt = new Bentley.GeometryNET.DPoint3d();
double prevZ = 0;
bool prevAbove = false;
foreach (var drapePt in drapeResult)
{
//Check Drapped point position i.e. inside terrain or outside terrain boundry
if (drapePt.Code == Bentley.TerrainModelNET.DTMDrapedLinearElementPointCode.External || drapePt.Code == Bentley.TerrainModelNET.DTMDrapedLinearElementPointCode.Void)
{
hasPrevPt = false;
continue;
}
Bentley.GeometryNET.DPoint3d newPt = drapePt.Coordinates;
double newZ = startPt.Z + (drapePt.DistanceAlong / vectorDist) * vectorZDiff;
bool newAbove = newZ > newPt.Z;
if (hasPrevPt)
{
if (newAbove != prevAbove)
{
//Create line segment from start point and end point of line
Bentley.GeometryNET.DSegment3d seg1 = new Bentley.GeometryNET.DSegment3d(startPt, endPt);
//Create line from drapped points from start point and end point on terrain
Bentley.GeometryNET.DSegment3d seg2 = new Bentley.GeometryNET.DSegment3d(prevPt, newPt);
double f1, f2;
//intersect both line segment and terrain segment
Bentley.GeometryNET.DSegment3d.ClosestApproachSegment(seg1, seg2, out intersectionSegment, out f1, out f2);
//Get intersection point from intersection line
Bentley.GeometryNET.DPoint3d intersectPt = intersectionSegment.StartPoint;
return true;
}
}
hasPrevPt = true;
prevPt = drapePt.Coordinates;
prevZ = newZ;
prevAbove = newAbove;
}
return false;
}
In the above code, Element el represents the LineString selected from the UI.